/*
 * Copyright (c) 2010 MoSync AB
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

extern "C"
{
#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>
#include "../toluabindings/tolua.h"
}

#include <maapi.h>
#include <MAUtil/Geometry.h>
#include <conprint.h>

#include "System.h"
#include "LuaInterpreter.h"
#include "EasyTextDownloader.h"
#include "EasyImageDownloader.h"
#include "EasyBinaryDownloader.h"

// #include <tolua/tolua.h>

// This function loads the tolua generated bindings to MoSync.
extern "C" TOLUA_API int tolua_lua_maapi_open (lua_State* tolua_S);

using namespace MAUtil;
using namespace MAUI;

namespace MAInterpreter
{

// ========== Helper functions ==========

/**
 * TODO: Document.
 */
static void setUserData(lua_State *L, const char* key, void* data)
{
	// Push key.
	// lua_pushlightuserdata(L, (void*)&key);  // Use a C pointer as a key
	lua_pushstring(L, key);

	// Push value.
	lua_pushlightuserdata(L, data);

	// Store in register.
	lua_settable(L, LUA_REGISTRYINDEX);
}

/**
 * TODO: Document.
 */
static void* getUserData(lua_State *L, const char* key)
{
	// Push key.
	lua_pushstring(L, key);

	// Get value.
	lua_gettable(L, LUA_REGISTRYINDEX);

	// Return value.
	void* data = (void*) lua_topointer(L, -1);

	// Pop value before returning it.
	lua_pop(L, 1);

	return data;
}

/**
 * Returns true if the stack element is an integer.
 */
static bool isinteger(lua_State *L, int narg)
{
	lua_Integer d = lua_tointeger(L, narg);
	if (d == 0 && !lua_isnumber(L, narg))
	{
		return false;
	}
	else
	{
		return true;
	}
}

static LuaInterpreter* getInterpreterInstance(lua_State *L)
{
    // Get pointer to interpreter instance from global Lua register.
    return (LuaInterpreter*) getUserData(L, "LuaInterpeterInstance");
}

static int getScreenWidth()
{
	MAExtent screenSize = maGetScrSize();
	return EXTENT_X(screenSize);
}

static int getScreenHeight()
{
	MAExtent screenSize = maGetScrSize();
	return EXTENT_Y(screenSize);
}

// ====== Class LuaConnListener ======

/**
 * This class listens to low-level connection events
 * and calls a Lua function with the event data.
 */
class LuaInterpreterConnectionListener : public ConnListener
{
private:
	lua_State *L;
	int closureRef;

public:
	LuaInterpreterConnectionListener(lua_State *L, int closureRef)
	{
		this->L = L;
		this->closureRef = closureRef;
	}

	virtual ~LuaInterpreterConnectionListener()
	{
		// Release callback function.
		luaL_unref(L, LUA_REGISTRYINDEX, closureRef);
	}

	virtual void connEvent(const MAConnEventData& data)
	{
		// Push callback function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closureRef);

		// Push data params. See MAConnEventData for details.
		lua_pushinteger(L, data.handle);
		lua_pushinteger(L, data.opType);
		lua_pushinteger(L, data.result);

		// Call function with 3 arguments and 0 results.
		lua_call(L, 3, 0);
	}
};

// ====== Class LuaInterpreterTextDownloader ======

class LuaInterpreterTextDownloader : public EasyTextDownloader
{
private:
	lua_State *L;
	int closureRef;

public:
	LuaInterpreterTextDownloader(lua_State *L, int closureRef)
	{
		this->L = L;
		this->closureRef = closureRef;
	}

	void onDownloadComplete(char* text)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closureRef);

		// Push data param (this is a string).
		if (!text)
		{
			lua_pushnil(L);
		}
		else
		{
			// This copies the text.
			lua_pushstring(L, text);
		}

		// Call function with 1 arguments and 0 results.
		lua_call(L, 1, 0);

		// Release function.
		luaL_unref(L, LUA_REGISTRYINDEX, closureRef);

		// Free the string data (it has been copied by Lua).
		if (text)
		{
			EasyTextDownloader::freeData(text);
		}
	}
};

// ====== Class LuaInterpreterImageDownloader ======

class LuaInterpreterImageDownloader : public EasyImageDownloader
{
private:
	lua_State *L;
	int closureRef;

public:
	LuaInterpreterImageDownloader(lua_State *L, int closureRef)
	{
		this->L = L;
		this->closureRef = closureRef;
	}

	void onDownloadComplete(MAHandle imageHandle)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closureRef);

		// Push the image handle, or nil, onto the stack.
		// It is up to the Lua application to free the image
		// using maDestroyObject.
		if (!imageHandle)
		{
			lua_pushnil(L);
		}
		else
		{
			lua_pushinteger(L, imageHandle);
		}

		// Call function with 1 arguments and 0 results.
		lua_call(L, 1, 0);

		// Release function.
		luaL_unref(L, LUA_REGISTRYINDEX, closureRef);
	}
};

// ====== Class LuaInterpreterBinaryDownloader ======

/**
 * This should be the only downloader we need! Do all the rest in Lua.
 */
class LuaInterpreterBinaryDownloader : public EasyBinaryDownloader
{
private:
	lua_State *L;
	int closureRef;

public:
	LuaInterpreterBinaryDownloader(lua_State *L, int closureRef)
	{
		this->L = L;
		this->closureRef = closureRef;
	}

	void onDownloadComplete(MAHandle data)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closureRef);

		// Push the image handle, or nil, onto the stack.
		// It is up to the Lua application to free the image
		// using maDestroyObject.
		if (!data)
		{
			lua_pushnil(L);
		}
		else
		{
			lua_pushinteger(L, data);
		}

		// Call function with 1 arguments and 0 results.
		lua_call(L, 1, 0);

		// Release function.
		luaL_unref(L, LUA_REGISTRYINDEX, closureRef);
	}
};

// ====== Class LuaInterpreterTextBoxListener ======

class LuaInterpreterTextBoxListener : public TextBoxListener
{
private:
	lua_State *L;
	int closureRef;

public:
	static const int TEXTBOX_RESULT_CAPACITY = 512;
	wchar textBoxResult[TEXTBOX_RESULT_CAPACITY];

public:
	LuaInterpreterTextBoxListener(lua_State *L, int closureRef)
	{
		this->L = L;
		this->closureRef = closureRef;
	}

	virtual ~LuaInterpreterTextBoxListener()
	{
		// Release function closure.
		luaL_unref(L, LUA_REGISTRYINDEX, closureRef);
	}

	void textBoxClosed(int result, int textLength)
	{
		// Push callback function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closureRef);

		// Push the text string, or nil, onto the stack.
		// The string will be copied into Lua space.
		if (MA_TB_RES_OK != result)
		{
			lua_pushnil(L);
		}
		else
		{
			// Convert text string.
			textBoxResult[textLength] = 0;
			char* str = System::wideCharToCharString(textBoxResult);
			if (str)
			{
				// Push string.
				lua_pushstring(L, str);

				free(str);
			}
		}

		// Call function with 1 arguments and 0 results.
		lua_call(L, 1, 0);

		// Remove me as listener.
		Environment::getEnvironment().removeTextBoxListener(this);

		// Free me.
		delete this;
	}
};

// ====== Class LuaInterpreterTimerListener ======

class LuaInterpreterTimerListener : public TimerListener
{
private:
	lua_State *L;
	int closureRef;

public:
	LuaInterpreterTimerListener(lua_State *L, int closureRef)
	{
		this->L = L;
		this->closureRef = closureRef;
	}

	virtual ~LuaInterpreterTimerListener()
	{
		// Release function closure.
		luaL_unref(L, LUA_REGISTRYINDEX, closureRef);
	}

	void runTimerEvent()
	{
		// Push callback function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closureRef);

		// Call function with 0 arguments and 0 results.
		lua_call(L, 0, 0);
	}
};

// ========== Implementation of Lua primitives ==========

// This is just a test!
// TODO: Remove.
static int luaBufferGetIntX(lua_State *L)
{
	if (!lua_isnoneornil(L, 1) && lua_islightuserdata(L, 1))
	{
		void* data = (void*) lua_touserdata(L, 1);
		int offset = luaL_checkint(L, 2);
		if (NULL != data)
		{
			int* p = (int*) data;
			// Push result.
			lprintfln("luaBufferGetIntX result: %i", p[offset]);
			lua_pushinteger(L, p[offset]);
			return 1; // Number of results
		}
	}

	// Error, push nil.
	lua_pushnil(L);
	return 1; // Number of results
}

/**
 * Print to console, e.g. the logcat output on Android.
 */
static int luaLog(lua_State *L)
{
	const char* message = luaL_checkstring(L, 1);
	lprintfln("%s", message);
	return 0; // Number of results
}

/**
 * Print to the device screen, and to the console.
 */
static int luaPrint(lua_State *L)
{
	const char* message = luaL_checkstring(L, 1);
	printf("%s", message);
	return 0; // Number of results
}


/**
 * Closes the entire application.
 */
static int luaQuitApplication(lua_State *L)
{
	maExit(0);
	return 0; // Number of results
}

/**
 * Exits the event loop of the Lua interpreter's moblet.
 * TODO: Rename to exitEventLoop() ?
 */
static int luaExit(lua_State *L)
{
	LuaInterpreter* interpreter = getInterpreterInstance(L);
	interpreter->_isRunning = false;
	return 0; // Number of results
}

static int luaResumeEventHandling(lua_State *L)
{
    LuaInterpreter* interpreter = getInterpreterInstance(L);
    interpreter->setDispatchEvents(true);
	return 0; // Number of results
}

static int luaPauseEventHandling(lua_State *L)
{
    LuaInterpreter* interpreter = getInterpreterInstance(L);
    interpreter->setDispatchEvents(false);
	return 0; // Number of results
}

/**
 * Convert the contents of a string pointer to a Lua string.
 */
static int luaToString(lua_State *L)
{
	// First param is pointer to text buffer, must not
	// be nil and must be light user data.
	if (!lua_isnoneornil(L, 1) && lua_islightuserdata(L, 1))
	{
		char* text = (char*) lua_touserdata(L, 1);
		if (NULL != text)
		{
			// This copies the text.
			lua_pushstring(L, text);
		}
	}

	return 1; // Number of results
}

static int luaScreenSetColor(lua_State *L)
{
	int red = luaL_checkint(L, 1);
	int green = luaL_checkint(L, 2);
	int blue = luaL_checkint(L, 3);

	int color = (red << 16) | (green << 8) | blue;

	maSetColor(color);

	return 0; // Number of results
}

// Old function that handles a clip rect.
// TODO: Possibly remove?
#if(0)
static int luaFillRect(lua_State *L)
{
	int left = luaL_checkint(L, 1);
	int top = luaL_checkint(L, 2);
	int width = luaL_checkint(L, 3);
	int height = luaL_checkint(L, 4);

    LuaInterpreter* interpreter = getInterpreterInstance(L);

    /*lprintfln("luaFillRect: %i %i %i %i",
		interpreter->getScreenLeft(),
		interpreter->getScreenTop(),
		interpreter->getScreenWidth(),
		interpreter->getScreenHeight());
	*/

	MARect clipRect;
	maGetClipRect(&clipRect);
	maSetClipRect(
		interpreter->getScreenLeft(),
		interpreter->getScreenTop(),
		interpreter->getScreenWidth(),
		interpreter->getScreenHeight());

	maFillRect(
		interpreter->getScreenLeft() + left,
		interpreter->getScreenTop() + top,
		width,
		height);
	//maUpdateScreen();

	maSetClipRect(clipRect.left, clipRect.top, clipRect.width, clipRect.height);

	return 0; // Number of results
}
#endif

static int luaScreenFillRect(lua_State *L)
{
	int left = luaL_checkint(L, 1);
	int top = luaL_checkint(L, 2);
	int width = luaL_checkint(L, 3);
	int height = luaL_checkint(L, 4);

	maFillRect(left, top, width, height);

	return 0; // Number of results
}

static int luaScreenDrawImage(lua_State *L)
{
	int image = luaL_checkint(L, 1);
	int left = luaL_checkint(L, 2);
	int top = luaL_checkint(L, 3);

	maDrawImage(image, left, top);

	return 0; // Number of results
}

static int luaScreenGetWidth(lua_State *L)
{
	int width = EXTENT_X(maGetScrSize());
	lua_pushnumber(L, width);  // push result
	return 1; // Number of results
}

static int luaScreenGetHeight(lua_State *L)
{
	int height = EXTENT_Y(maGetScrSize());
	lua_pushnumber(L, height);  // push result
	return 1; // Number of results
}

static int luaScreenGetImageWidth(lua_State *L)
{
	int image = luaL_checkint(L, 1);
	int width = EXTENT_X(maGetImageSize(image));
	lua_pushnumber(L, width);  // push result
	return 1; // Number of results
}

static int luaScreenGetImageHeight(lua_State *L)
{
	int image = luaL_checkint(L, 1);
	int height = EXTENT_Y(maGetImageSize(image));
	lua_pushnumber(L, height);  // push result
	return 1; // Number of results
}

static int luaScreenUpdate(lua_State *L)
{
	maUpdateScreen();

	return 0; // Number of results
}

// TODO: Unused version that clips drawing area. Remove.
#if(0)
static int luaDrawText(lua_State *L)
{
	// Get args.
	const char* text = luaL_checkstring(L, 1);
	int x = luaL_checkint(L, 2);
	int y = luaL_checkint(L, 3);
	const char* fontName = luaL_checkstring(L, 4);

	// Find font.
	LuaInterpreter* interpreter = getInterpreterInstance(L);
	Font* font = Sys()->getFont(fontName);
	if (NULL == font)
	{
		return 0;
	}

	MARect clipRect;
	maGetClipRect(&clipRect);
	maSetClipRect(
		interpreter->getScreenLeft(),
		interpreter->getScreenTop(),
		interpreter->getScreenWidth(),
		interpreter->getScreenHeight());

	// Draw string.
	Rect bounds(
		interpreter->getScreenLeft(),
		interpreter->getScreenTop(),
		interpreter->getScreenWidth(),
		interpreter->getScreenHeight());
	font->drawBoundedString(
		text,
		interpreter->getScreenLeft() + x,
		interpreter->getScreenTop() + y,
		bounds);
	//maUpdateScreen();

	maSetClipRect(clipRect.left, clipRect.top, clipRect.width, clipRect.height);

	return 0; // Number of results
}
#endif

static int luaScreenDrawText(lua_State *L)
{
	// Get args.
	const char* text = luaL_checkstring(L, 1);
	int x = luaL_checkint(L, 2);
	int y = luaL_checkint(L, 3);
	const char* fontName = luaL_checkstring(L, 4);

	// Find font, font must be registered in the System class.
	LuaInterpreter* interpreter = getInterpreterInstance(L);
	Font* font = Sys()->getFont(fontName);
	if (NULL == font)
	{
		lprintfln("luaScreenDrawText: Font not found: %s", fontName);
		return 0;
	}

	lprintfln("luaScreenDrawText: Drawing text: %s", text);

	// Draw string.
	Rect bounds(0, 0, EXTENT_X(maGetScrSize()), EXTENT_Y(maGetScrSize()));
	font->drawBoundedString(text, x, y, bounds);

	return 0; // Number of results
}

static int luaDownloadText(lua_State *L)
{
	// URL to download from.
	const char* url = luaL_checkstring(L, 1);

	// Second arg must be a function.
	if (LUA_TFUNCTION == lua_type(L, 2))
	{
		// Store function closure in the registry.
		lua_pushvalue(L, 2);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);
		LuaInterpreterTextDownloader* downloader =
			new LuaInterpreterTextDownloader(L, closureRef);

		// Begin download.
		downloader->get(url);

		// See LuaInterpreterTextDownloader for code that calls the
		// closure when the text data has been downloaded.
		// The function closure is deallocated by the downloader.
	}

	return 0; // Number of return values
}

static int luaDownloadImage(lua_State *L)
{
	// URL to download from.
	const char* url = luaL_checkstring(L, 1);

	// Second arg must be a function.
	if (LUA_TFUNCTION == lua_type(L, 2))
	{
		// Store function closure in the registry.
		lua_pushvalue(L, 2);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);
		LuaInterpreterImageDownloader* downloader =
			new LuaInterpreterImageDownloader(L, closureRef);

		// Begin download.
		downloader->get(url);

		// See LuaInterpreterImageDownloader for code that calls the
		// closure when the image has been downloaded.
	}

	return 0; // Number of return values
}

static int luaDownloadBinaryData(lua_State *L)
{
	// URL to download from.
	const char* url = luaL_checkstring(L, 1);

	// Second arg must be a function.
	if (LUA_TFUNCTION == lua_type(L, 2))
	{
		// Store function closure in the registry.
		lua_pushvalue(L, 2);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);
		LuaInterpreterBinaryDownloader* downloader =
			new LuaInterpreterBinaryDownloader(L, closureRef);

		// Begin download.
		downloader->get(url);

		// See LuaInterpreterBinaryDownloader for code that calls the
		// closure when the image has been downloaded.
	}

	return 0; // Number of return values
}

static int luaSetConnectionFun(lua_State *L)
{
	// First arg is the connection handle.
	MAHandle connectionHandle = (MAHandle) luaL_checkint(L, 1);

	// Second arg must be a function.
	if (LUA_TFUNCTION == lua_type(L, 2))
	{
		// Store function closure in the registry.
		lua_pushvalue(L, 2);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Create listener object.
		LuaInterpreterConnectionListener* connectionListener =
			new LuaInterpreterConnectionListener(L, closureRef);

		// Add listener to table. We do this to get a reference from
		// the connection handle to the listener. This is used when
		// removing the listener, to deallocate it.
		LuaInterpreter* interpreter = getInterpreterInstance(L);
		interpreter->connectionListeners[connectionHandle] =
			connectionListener;

		// Register listener with the MoSync environment to
		// hook into the connection event callback mechanism.
		Environment::getEnvironment().setConnListener(
			connectionHandle,
			connectionListener);
	}

	return 0; // Number of return values
}

static int luaRemoveConnectionFun(lua_State *L)
{
	// First and only arg is the connection handle.
	MAHandle connectionHandle = (MAHandle) luaL_checkint(L, 1);

	// Get the listener.
	LuaInterpreter* interpreter = getInterpreterInstance(L);
	LuaInterpreterConnectionListener* connectionListener =
		(LuaInterpreterConnectionListener*)
			interpreter->connectionListeners[connectionHandle];

	// Remove listener from to table.
	interpreter->connectionListeners.erase(connectionHandle);

	// Delete the listener. This also releases the Lua callback function.
	delete connectionListener;

	return 0; // Number of return values
}

/**
 * Params:
 *  Title of textbox.
 *  Default text.
 *  Callback function(nil/string)
 */
static int luaOpenTextBox(lua_State *L)
{
	// Get the title.
	const char* title = luaL_checkstring(L, 1);

	// Get the default text.
	const char* text = luaL_checkstring(L, 2);

	// Convert to wchar.
	wchar* wtitle = System::charToWideCharString(title);
	wchar* wtext = System::charToWideCharString(text);

	// Third arg must be a function, and we must have
	// non-null strings.
	if (LUA_TFUNCTION == lua_type(L, 3) && wtitle && wtext)
	{
		// Store function closure in the registry.
		lua_pushvalue(L, 3);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Create textbox listener.
		LuaInterpreterTextBoxListener* textBoxListener =
			new LuaInterpreterTextBoxListener(L, closureRef);

		int result = maTextBox(
			wtitle,
			wtext,
			textBoxListener->textBoxResult,
			textBoxListener->TEXTBOX_RESULT_CAPACITY,
			MA_TB_TYPE_ANY);
		if (IOCTL_UNAVAILABLE == result)
		{
			lprintfln("luaOpenTextBox: maTextBox is not available");

			// Deallocate textbox listener, will free closure ref.
			delete textBoxListener;
		}
		else
		{
			Environment::getEnvironment().addTextBoxListener(textBoxListener);
			lprintfln("luaOpenTextBox: maTextBox opened");
		}
	}

	if (wtitle) { free(wtitle); }
	if (wtext) { free(wtext); }

	return 0; // Number of return values
}

static int luaSetTouchDownFun(lua_State *L)
{
	// Do we have a closure?
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		// Get a closure reference.
		lua_pushvalue(L, 1);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Get interpreter.
		LuaInterpreter* interpreter = getInterpreterInstance(L);

		// Set the function.
		interpreter->setTouchDownFun(closureRef);
	}

	return 0; // Number of return values
}

static int luaSetTouchUpFun(lua_State *L)
{
	// Do we have a closure?
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		// Get a closure reference.
		lua_pushvalue(L, 1);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Get interpreter.
		LuaInterpreter* interpreter = getInterpreterInstance(L);

		// Set the function.
		interpreter->setTouchUpFun(closureRef);
	}

	return 0; // Number of return values
}

static int luaSetTouchDragFun(lua_State *L)
{
	// Do we have a closure?
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		// Get a closure reference.
		lua_pushvalue(L, 1);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Get interpreter.
		LuaInterpreter* interpreter = getInterpreterInstance(L);

		// Set the function.
		interpreter->setTouchDragFun(closureRef);
	}

	return 0; // Number of return values
}

static int luaSetKeyDownFun(lua_State *L)
{
	// Do we have a closure?
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		// Get a closure reference.
		lua_pushvalue(L, 1);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Get interpreter.
		LuaInterpreter* interpreter = getInterpreterInstance(L);

		// Set the function.
		interpreter->setKeyDownFun(closureRef);
	}

	return 0; // Number of return values
}

static int luaSetCustomEventFun(lua_State *L)
{
	// Do we have a closure?
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		// Get a closure reference.
		lua_pushvalue(L, 1);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Get interpreter.
		LuaInterpreter* interpreter = getInterpreterInstance(L);

		// Set the function.
		interpreter->setCustomEventFun(closureRef);
	}

	return 0; // Number of return values
}

static int luaSetCloseFun(lua_State *L)
{
	// Do we have a closure?
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		// Get a closure reference.
		lua_pushvalue(L, 1);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);

		// Get interpreter.
		LuaInterpreter* interpreter = getInterpreterInstance(L);

		// Set the function.
		interpreter->setCloseFun(closureRef);
	}

	return 0; // Number of return values
}

/**
 * Params:
 *  Millisecond interval.
 *  Callback function.
 * Returns:
 *  Pointer to timer listener. Use this when
 *  removing the timer.
 */
static int luaAddTimer(lua_State *L)
{
	// First arg must be an int, second arg must be a function.
	if (isinteger(L, 1) && LUA_TFUNCTION == lua_type(L, 2))
	{
		// Get timer interval.
		int interval = lua_tointeger(L, 1);

		// Store function closure in the registry.
		lua_pushvalue(L, 2);
		int closureRef = luaL_ref(L, LUA_REGISTRYINDEX);
		LuaInterpreterTimerListener* timerListener =
			new LuaInterpreterTimerListener(L, closureRef);

		// Add timer.
		Environment::getEnvironment().addTimer(timerListener, interval, 0);

		// Push pointer to timer listener.
		lua_pushlightuserdata(L, timerListener);
	}
	else
	{
		// Error, push nil.
		lua_pushnil(L);
	}

	return 1; // Number of return values
}

/**
 * Params:
 *  Pointer to timer listener.
 */
static int luaRemoveTimer(lua_State *L)
{
	// First arg is pointer to timer, must not be nil and
	// must be light user data.
	if (!lua_isnoneornil(L, 1) && lua_islightuserdata(L, 1))
	{
		LuaInterpreterTimerListener* timerListener =
			(LuaInterpreterTimerListener*) lua_touserdata(L, 1);
		if (NULL != timerListener)
		{
			// Remove timer.
			Environment::getEnvironment().removeTimer(timerListener);

			// Delete timer, will free the timer's closure reference.
			delete timerListener;
		}
	}

	return 0; // Number of return values
}

// Demo of how to call a C primitive with a Lua function closure.
// As an example assume luaCallFunctionClosure is bound to name
// "Call" in Lua, then we can write: Call(function(data) Log(data) end)
// and "Hello World" in printed.
static int luaDemoOfCallFunctionClosure(lua_State *L)
{
	// if (lua_isfunction(L, 1))
	if (LUA_TFUNCTION == lua_type(L, 1))
	{
		lua_pushvalue(L, 1);
		lua_pushstring(L, "Hello World");
		lua_call(L, 1, 0); // Call function with 1 arguments and 0 results
	}
	return 0; // Number of results
}

/**
 * Define a global table if it does not exist.
 */
static void ensureThatGlobalTableExists(
	lua_State *L,
	const char* tableName)
{
	// Push global or nil onto the stack.
	lua_getglobal(L, tableName);

	// Does the global variable exist?
	if (lua_isnoneornil(L, -1))
	{
		// It does not exist. Push new table onto stack.
		lua_newtable(L);

		// Define it as a global variable. This pops the table off the stack.
		lua_setglobal(L, tableName);
	}

	// Important to pop the initial global element.
	lua_pop(L, 1);
}

static void RegTableFun(
	lua_State* L,
	const char* tableName,
	const char* funName,
	lua_CFunction funPointer)
{
	ensureThatGlobalTableExists(L, tableName);

	// Push table onto stack.
	lua_getglobal(L, tableName);

	// Push table key.
	lua_pushstring(L, funName);

	// Push value.
	lua_pushcfunction(L, funPointer);

	// Set table entry. Pops value and key.
	lua_rawset(L, -3);

	// Pop table off the stack.
	lua_pop(L, 1);
}

static void RegFun(
	lua_State* L,
	const char* funName,
	lua_CFunction funPointer)
{
	lua_pushcfunction(L, funPointer);
	lua_setglobal(L, funName);
}

static void registerNativeFunctions(lua_State* L)
{
	RegFun(L, "luaBufferGetIntX", luaBufferGetIntX);
	RegFun(L, "print", luaPrint);
	RegFun(L, "SysBufferToString", luaToString);
	RegTableFun(L, "System", "log", luaLog);
	RegTableFun(L, "System", "quit", luaQuitApplication);
	RegTableFun(L, "System", "exit", luaExit);
	RegTableFun(L, "System", "pauseEventHandling", luaPauseEventHandling);
	RegTableFun(L, "System", "resumeEventHandling", luaResumeEventHandling);
	RegTableFun(L, "System", "downloadText", luaDownloadText);
	RegTableFun(L, "System", "downloadImage", luaDownloadImage);
	RegTableFun(L, "System", "downloadBinaryData", luaDownloadBinaryData);
	//RegTableFun("System", "downloadSound", luaDownloadSound);
	RegTableFun(L, "System", "setConnectionFun", luaSetConnectionFun);
	RegTableFun(L, "System", "removeConnectionFun", luaRemoveConnectionFun);
	RegTableFun(L, "System", "openTextBox", luaOpenTextBox);
	RegTableFun(L, "System", "onTouchDown", luaSetTouchDownFun);
	RegTableFun(L, "System", "onTouchUp", luaSetTouchUpFun);
	RegTableFun(L, "System", "onTouchDrag", luaSetTouchDragFun);
	RegTableFun(L, "System", "onKeyDown", luaSetKeyDownFun);
	RegTableFun(L, "System", "onKeyUp", luaSetKeyDownFun);
	//RegTableFun(L, "System", "onFocusGained", luaSetFocusGainedFun);
	//RegTableFun(L, "System", "onFocusLost", luaSetFocusLostFun);
	RegTableFun(L, "System", "onCustomEvent", luaSetCustomEventFun);
	RegTableFun(L, "System", "onClose", luaSetCloseFun);
	RegTableFun(L, "System", "startTimer", luaAddTimer);
	RegTableFun(L, "System", "stopTimer", luaRemoveTimer);
	RegTableFun(L, "Screen", "getWidth", luaScreenGetWidth);
	RegTableFun(L, "Screen", "getHeight", luaScreenGetHeight);
	RegTableFun(L, "Screen", "getImageWidth", luaScreenGetImageWidth);
	RegTableFun(L, "Screen", "getImageHeight", luaScreenGetImageHeight);
	RegTableFun(L, "Screen", "drawImage", luaScreenDrawImage);
	RegTableFun(L, "Screen", "setColor", luaScreenSetColor);
	RegTableFun(L, "Screen", "fillRect", luaScreenFillRect);
	RegTableFun(L, "Screen", "drawText", luaScreenDrawText);
	RegTableFun(L, "Screen", "update", luaScreenUpdate);
/*	RegTableFun(L, "Interpreter", "create", luaInterpreterCreate);
	RegTableFun(L, "Interpreter", "delete", luaInterpreterDelete);
	RegTableFun(L, "Interpreter", "eval", luaInterpreterEval);
	RegTableFun(L, "Interpreter", "evalResource", luaInterpreterEvalResource);
	RegTableFun(L, "Interpreter", "onTouchDown", luaInterpreterOnTouchDown);
	RegTableFun(L, "Interpreter", "onTouchUp", luaInterpreterOnTouchUp);
	RegTableFun(L, "Interpreter", "onTouchDrag", luaInterpreterOnTouchDrag);
	RegTableFun(L, "Interpreter", "onKeyDown", luaInterpreterOnKeyDown);
	RegTableFun(L, "Interpreter", "onKeyUp", luaInterpreterOnKeyUp);
	RegTableFun(L, "Interpreter", "onCustomEvent", luaInterpreterOnCustomEvent);
	RegTableFun(L, "Interpreter", "onClose", luaInterpreterOnClose);
*/
}

// ========== Constructor/Destructor ==========

LuaInterpreter::LuaInterpreter() :
	L(NULL),
	touchDownFun(0),
	touchUpFun(0),
	touchDragFun(0),
	keyUpFun(0),
	keyDownFun(0),
	customEventFun(0),
	closeFun(0),
	_isRunning(true)
{
}

LuaInterpreter::~LuaInterpreter()
{
	close();
}

// ========== Methods ==========

/**
 * Returns non-zero if successful.
 */
int LuaInterpreter::initialize()
{
	// Deallocate previous Lua state, if it exists.
	if (L)
	{
		lua_close(L);
		L = NULL;
	}

	// Create Lua state.
	L = lua_open();
	if (!L)
	{
		return 0;
	}

	luaL_openlibs(L);

	tolua_lua_maapi_open(L);

	registerNativeFunctions(L);

	// Now we save a pointer to the interpreter instance in the
	// global Lua register. This is used to set the clock interval
	// of the interpreter instance from a Lua primitive.
	setUserData(L, "LuaInterpeterInstance", (void*) this);

	return 1;
}

void LuaInterpreter::close()
{
	if (L)
	{
		lua_close(L);
		L = NULL;
	}

	// TODO: Free function closures.
	// We can skip this as we close the entire interpreter, but
	// remember to free old functions when new ones are set.
}

/**
 * Returns non-zero if successful, zero on error.
 */
int LuaInterpreter::eval(const char* script)
{
	// Evaluate Lua script.
	int result = luaL_dostring(L, script);

	// Was there an error?
	if (0 != result)
	{
		String errorMessage;

    	if (lua_isstring(L, -1))
    	{
    		errorMessage = lua_tostring(L, -1);

            // Pop the error message.
        	lua_pop(L, 1);
    	}
    	else
    	{
    		errorMessage =
    			"There was a Lua error condition, but no error message.";
    	}

        lprintfln("Lua Error: %s\n", errorMessage.c_str());

    	// Print size of Lua stack (debug info).
    	lprintfln("Lua stack size: %i\n", lua_gettop(L));

    	reportLuaInterpreterError(errorMessage.c_str());
	}

	return result == 0;
}

/**
 * Helper method that evaluates a text string from resource file.
 * Returns non-zero if successful, zero on error.
 */
int LuaInterpreter::evalResource(MAHandle scriptResourceId)
{
	char* script = System::loadTextResource(scriptResourceId);
	if (script)
	{
		int result = eval(script);
		free(script);
		return result;
	}
	else
	{
		return 0;
	}
}

void LuaInterpreter::reportLuaInterpreterError(const char* errorMessage)
{
	reportInterpreterError(errorMessage);
}

// ========== Event handling methods ==========

// TODO: Do we need to check if event dispatching is off
// in the following event handlers?
// Review how _isRunning is used.

void LuaInterpreter::setTouchDownFun(int closureRef)
{
	if (touchDownFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, touchDownFun);
	}

	touchDownFun = closureRef;
}

void LuaInterpreter::setTouchUpFun(int closureRef)
{
	if (touchUpFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, touchUpFun);
	}

	touchUpFun = closureRef;
}

void LuaInterpreter::setTouchDragFun(int closureRef)
{
	if (touchDragFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, touchDragFun);
	}

	touchDragFun = closureRef;
}

void LuaInterpreter::setKeyDownFun(int closureRef)
{
	if (keyDownFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, keyDownFun);
	}

	keyDownFun = closureRef;
}

void LuaInterpreter::setKeyUpFun(int closureRef)
{
	if (keyUpFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, keyUpFun);
	}

	keyUpFun = closureRef;
}

void LuaInterpreter::setCustomEventFun(int closureRef)
{
	if (customEventFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, customEventFun);
	}

	customEventFun = closureRef;
}

void LuaInterpreter::setCloseFun(int closureRef)
{
	if (closeFun)
	{
		// Release old function.
		luaL_unref(L, LUA_REGISTRYINDEX, closeFun);
	}

	closeFun = closureRef;
}

void LuaInterpreter::onTouchDown(int x, int y, int touchId)
{
	if (touchDownFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, touchDownFun);

		// Push arguments.
		lua_pushinteger(L, x);
		lua_pushinteger(L, y);
		lua_pushinteger(L, touchId);

		// Call function with 3 arguments and 0 results.
		lua_call(L, 3, 0);
	}
}

void LuaInterpreter::onTouchUp(int x, int y, int touchId)
{
	if (touchUpFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, touchUpFun);

		// Push arguments.
		lua_pushinteger(L, x);
		lua_pushinteger(L, y);
		lua_pushinteger(L, touchId);

		// Call function with 3 arguments and 0 results.
		lua_call(L, 3, 0);
	}
}

void LuaInterpreter::onTouchDrag(int x, int y, int touchId)
{
	if (touchDragFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, touchDragFun);

		// Push arguments.
		lua_pushinteger(L, x);
		lua_pushinteger(L, y);
		lua_pushinteger(L, touchId);

		// Call function with 3 arguments and 0 results.
		lua_call(L, 3, 0);
	}
}

void LuaInterpreter::onKeyDown(int keyCode, int nativeKeyCode)
{
	if (keyDownFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, keyDownFun);

		// Push arguments.
		lua_pushinteger(L, keyCode);
		lua_pushinteger(L, nativeKeyCode);

		// Call function with 2 arguments and 0 results.
		lua_call(L, 2, 0);
	}
}

void LuaInterpreter::onKeyUp(int keyCode, int nativeKeyCode)
{
	if (keyUpFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, keyUpFun);

		// Push arguments.
		lua_pushinteger(L, keyCode);
		lua_pushinteger(L, nativeKeyCode);

		// Call function with 2 arguments and 0 results.
		lua_call(L, 2, 0);
	}
}

void LuaInterpreter::onCustomEvent(MAEvent* event)
{
	if (customEventFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, customEventFun);

		// Push event pointer.
		lua_pushlightuserdata(L, event);
		//tolua_pushusertype(L, (void*)event, "MAEvent");

		// Call function with 1 arguments and 0 results.
		lua_call(L, 1, 0);
	}
}

void LuaInterpreter::onClose()
{
	if (closeFun)
	{
		// Push function.
		lua_rawgeti(L, LUA_REGISTRYINDEX, closeFun);

		// Call function with 0 arguments and 0 results.
		lua_call(L, 0, 0);
	}
}

bool LuaInterpreter::isRunning()
{
	return _isRunning;
}

/**
 * Currently not used! Keep as a reference of how to call global Lua functions.
 *
 * Helper function that calls a named Lua function with 0..2 numeric arguments.
 */
/*
void LuaInterpreter::callLuaFunctionWithNumericArguments(
	const char* funName,
	int numberOfArguments,
	int arg1,
	int arg2)
{
	//lprintfln("Calling %s", funName);

	// Print size of Lua stack (debug info).
	//lprintfln("Lua stack size: %i\n", lua_gettop(L));
	if (lua_gettop(L) > 0)
	{
		lprintfln("Lua stack size: %i Calling maPanic\n", lua_gettop(L));
		maPanic(lua_gettop(L), "Lua stack size is bigger than zero.");
	}

	// Exit if event dispatching is off.
	if (!getDispatchEvents())
	{
		return;
	}

	// Get the event function to call, exit if the function is not defined.
	lua_getglobal(L, funName);
	if (lua_isnoneornil(L, -1))
	{
		//lprintfln("%s is not defined", funName);

		// Important to pop the top element before we return.
		lua_pop(L, 1);

		return;
	}

	// Push arguments
	if (numberOfArguments > 0) { lua_pushnumber(L, arg1); }
	if (numberOfArguments > 1) { lua_pushnumber(L, arg2); }

    // Do the call (Lua state, number of arguments, 0 results, 0 for standard error handling)
    if (lua_pcall(L, numberOfArguments, 0, 0))
    {
    	String errorMessage;

    	lprintfln("Error calling: %s", funName);

    	// Print stack for debugging.
    	int stackSize = lua_gettop(L);
    	for (int i = stackSize; i > 0; --i)
    	{
    		if (lua_isstring(L, i))
    		{
    			lprintfln("Stack element: %i is string: %s", i, lua_tostring(L, i));
    		}
    		else
    		{
    			int type = lua_type(L, i);
    			lprintfln("Stack element: %i has type: %i", i, type);
    		}
    	}

    	if (lua_isstring(L, -1))
    	{
    		errorMessage = lua_tostring(L, -1);

            // Pop the error message.
        	lua_pop(L, 1);
    	}
    	else
    	{
    		errorMessage =
    			"There was a Lua error condition, but no error message.";
    	}

    	// Empty stack.
    	while (lua_gettop(L) > 0)
		{
    		lprintfln("Popping stack element");
    		lua_pop(L, 1);
		}

	    lprintfln("Lua Error: %s\n", errorMessage.c_str());

    	reportLuaInterpreterError(errorMessage.c_str());
    }
}
*/

}

